// Scintilla source code edit control
/** @file CharClassify.h
 ** Character classifications used by Document and RESearch.
 **/
// Copyright 2006-2009 by Neil Hodgson <neilh@scintilla.org>
// The License.txt file describes the conditions under which this software may be distributed.
#pragma once

namespace Scintilla::Internal {

constexpr int cp932 = 932;
constexpr int cp936 = 936;
constexpr int cp949 = 949;
constexpr int cp950 = 950;
constexpr int cp1361 = 1361;

bool DBCSIsLeadByte(int codePage, unsigned char uch) noexcept;
bool DBCSIsTrailByte(int codePage, unsigned char trail) noexcept;

constexpr bool IsDBCSCodePage(int codePage) noexcept {
	return codePage == cp932
		|| codePage == cp936
		|| codePage == cp949
		|| codePage == cp950
		|| codePage == cp1361;
}

constexpr bool IsDBCSValidSingleByte(int codePage, int ch) noexcept {
	switch (codePage) {
	case cp932:
		return ch == 0x80
			|| (ch >= 0xA0 && ch <= 0xDF)
			|| (ch >= 0xFD);

	default:
		return false;
	}
}

// Calculate a number from a DBCS byte pair that can be used to index into an array or map.
// Should only be called with genuine DBCS character pairs which means that ch1 has top bit set.
constexpr uint16_t DBCSIndex(unsigned char ch1, unsigned char uch2) noexcept {
	const unsigned char uch1 = ch1 & 0x7F;
	return (uch1 << 8) | uch2;
}

struct DBCSPair {
	char chars[2]{};
};
using FoldMap = std::array<DBCSPair, 0x8000>;

//grapheme type++Autogenerated -- start of section automatically generated
enum class GraphemeBreakProperty {
	Other = 0,
	Control = 1,
	Extend = 2,
	ZeroWidthJoiner = 3,
	Prepend = 4,
	HangulL = 5,
	HangulV = 6,
	HangulT = 7,
	HangulLV = 8,
	HangulLVT = 9,
	ExtendedPictographic = 10,
	RegionalIndicator = 11,
	ConjunctLinker = 12,
	LinkingConsonant = 13,
	ForwardSentinel = Prepend,
	BackwardSentinel = Extend,
};

constexpr int maxUnicodeGraphemeBreakCharacter = 0xe1000;
constexpr int longestUnicodeCharacterSequenceCount = 10;
constexpr int longestUnicodeCharacterSequenceBytes = 35;

constexpr uint16_t graphemeClusterBoundary[] = {
0b10101111'11110011, // Other
0b11111111'11111111, // Control
0b10101111'11110011, // Extend
0b10001011'11110011, // ZeroWidthJoiner
0b10000000'00000010, // Prepend
0b10101100'10010011, // HangulL
0b10101111'00110011, // HangulV
0b10101111'01110011, // HangulT
0b10101111'00110011, // HangulLV
0b10101111'01110011, // HangulLVT
0b10101111'11110011, // ExtendedPictographic
0b10100111'11110011, // RegionalIndicator
0b10001111'11110011, // ConjunctLinker
0b10101111'11110011, // LinkingConsonant
};

constexpr bool IsGraphemeClusterBoundary(GraphemeBreakProperty prev, GraphemeBreakProperty current) noexcept {
	return (graphemeClusterBoundary[static_cast<int>(prev)] >> (static_cast<int>(current))) & true;
}
//grapheme type--Autogenerated -- end of section automatically generated

class CharClassify {
public:
	CharClassify() noexcept;

	void SetDefaultCharClasses(bool includeWordClass) noexcept;
	void SetCharClasses(const unsigned char *chars, CharacterClass newCharClass) noexcept;
	void SetCharClassesEx(const unsigned char *chars, size_t length) noexcept;
	int GetCharsOfClass(CharacterClass characterClass, unsigned char *buffer) const noexcept;
	CharacterClass GetClass(unsigned char ch) const noexcept {
		return static_cast<CharacterClass>(charClass[ch]);
	}
	bool IsWord(unsigned char ch) const noexcept {
		return GetClass(ch) == CharacterClass::word;
	}

	static void InitUnicodeData() noexcept;

//++Autogenerated -- start of section automatically generated
// Created with Python 3.15.0a1, Unicode 17.0.0
	static CharacterClass ClassifyCharacter(uint32_t ch) noexcept {
		if (ch < sizeof(classifyMap)) {
			return static_cast<CharacterClass>(classifyMap[ch]);
		}
		if (ch >= 0xe01f0) {
			return CharacterClass::space; // Co, Cn
		}

		ch -= sizeof(classifyMap);
		ch = (CharClassifyTable[ch >> 11] << 8) | (ch & 2047);
		ch = (CharClassifyTable[(ch >> 6) + 417] << 6) | (ch & 63);
		ch = (CharClassifyTable[(ch >> 3) + 1417] << 3) | (ch & 7);
		return static_cast<CharacterClass>(CharClassifyTable[ch + 3057]);
	}
//--Autogenerated -- end of section automatically generated

//grapheme function++Autogenerated -- start of section automatically generated
	static GraphemeBreakProperty GetGraphemeBreakProperty(uint32_t ch) noexcept {
		if (ch < sizeof(graphemeMap)) {
			return static_cast<GraphemeBreakProperty>(graphemeMap[ch]);
		}
		if (ch >= maxUnicodeGraphemeBreakCharacter) {
			return GraphemeBreakProperty::Other;
		}

		ch -= sizeof(graphemeMap);
		ch = (GraphemeBreakTable[ch >> 12] << 8) | (ch & 4095);
		ch = (GraphemeBreakTable[(ch >> 7) + 221] << 6) | (ch & 127);
		ch = (GraphemeBreakTable[(ch >> 3) + 733] << 3) | (ch & 7);
		return static_cast<GraphemeBreakProperty>(GraphemeBreakTable[ch + 2365]);
	}
//grapheme function--Autogenerated -- end of section automatically generated

private:
	static constexpr uint32_t maxUnicode = 0x10ffff;
	static const uint8_t CharClassifyTable[];
	static const uint8_t GraphemeBreakTable[];
	static uint8_t classifyMap[0xffff + 1];
	static uint8_t graphemeMap[0x4000];

	static constexpr int maxChar = 256;
	uint8_t charClass[maxChar];
};

struct DBCSByteMask {
	uint8_t byteMask[256];
	bool IsLeadByte(unsigned char ch) const noexcept {
		return byteMask[ch] & true;
	}
	bool IsTrailByte(unsigned char ch) const noexcept {
		return byteMask[ch] & 2;
	}
};

class DBCSCharClassify {
public:
	explicit DBCSCharClassify(int codePage_) noexcept;

	bool IsLeadByte(unsigned char ch) const noexcept {
		return byteMask.IsLeadByte(ch);
	}
	bool IsTrailByte(unsigned char ch) const noexcept {
		return byteMask.IsTrailByte(ch);
	}
	const DBCSByteMask& GetByteMask() const noexcept {
		return byteMask;
	}

	CharacterClass ClassifyCharacter(uint32_t ch) const noexcept {
		if (ch < sizeof(classifyMap)) {
			return static_cast<CharacterClass>(classifyMap[ch]);
		}
		// Cn
		return CharacterClass::space;
	}

private:
	DBCSByteMask byteMask;
	unsigned char classifyMap[0xffff + 1];
};

}
